summaryrefslogtreecommitdiff
path: root/app/[lng]/admin/ecc/page.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-10-24 11:42:16 +0900
committerjoonhoekim <26rote@gmail.com>2025-10-24 11:42:16 +0900
commit912e2bc761d7d57bd33d68cb5050da43dea59636 (patch)
tree356268495639e9181f201ae01b5e66ca8dbc3c0d /app/[lng]/admin/ecc/page.tsx
parent4fcbdaff827e298ecd8a57939a287f61cc1b30d6 (diff)
(김준회) ecc/mdg 테스트 페이지 수정
Diffstat (limited to 'app/[lng]/admin/ecc/page.tsx')
-rw-r--r--app/[lng]/admin/ecc/page.tsx474
1 files changed, 365 insertions, 109 deletions
diff --git a/app/[lng]/admin/ecc/page.tsx b/app/[lng]/admin/ecc/page.tsx
index 257ecfea..cd09e213 100644
--- a/app/[lng]/admin/ecc/page.tsx
+++ b/app/[lng]/admin/ecc/page.tsx
@@ -10,7 +10,7 @@ import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { Alert, AlertDescription } from '@/components/ui/alert'
-import { Loader2, Play, CheckCircle, XCircle, Send } from 'lucide-react'
+import { Loader2, Play, CheckCircle, XCircle, Send, Plus, Minus } from 'lucide-react'
import { toast } from 'sonner'
// SOAP 송신 함수들 import
@@ -125,36 +125,96 @@ export default function ECCSenderTestPage() {
// PO 생성 테스트
const [poData, setPoData] = useState({
- // 헤더 정보
- ANFNR: 'TEST001',
- LIFNR: '1000000001',
- ZPROC_IND: 'A',
- ANGNR: 'TEST001',
- WAERS: 'KRW',
- ZTERM: '0001',
- INCO1: 'FOB',
- INCO2: 'Seoul, Korea',
- MWSKZ: 'V0',
- LANDS: 'KR',
- ZRCV_DT: '20241201',
- ZATTEN_IND: 'Y',
- IHRAN: '20241201',
- TEXT: 'Test PO Creation',
- // 아이템 정보
- ANFPS: '00001',
- NETPR: '1000.00',
- PEINH: '1',
- BPRME: 'EA',
- NETWR: '1000.00',
- BRTWR: '1100.00',
- LFDAT: '20241201',
- // PR 반환 정보
- EBELN: 'PR001',
- EBELP: '00001',
- MSGTY: 'S',
- MSGTXT: 'Test message'
+ header: {
+ ANFNR: 'TEST001',
+ LIFNR: '1000000001',
+ ZPROC_IND: 'A',
+ ANGNR: 'TEST001',
+ WAERS: 'KRW',
+ ZTERM: '0001',
+ INCO1: 'FOB',
+ INCO2: 'Seoul, Korea',
+ MWSKZ: 'V0',
+ LANDS: 'KR',
+ ZRCV_DT: '20241201',
+ ZATTEN_IND: 'Y',
+ IHRAN: '20241201',
+ TEXT: 'Test PO Creation',
+ LSTEL: '',
+ VSTEL: '',
+ ZDLV_CNTLR: '',
+ ZDLV_PRICE_NOTE: '',
+ ZDLV_PRICE_T: ''
+ },
+ items: [{
+ ANFNR: 'TEST001',
+ ANFPS: '00001',
+ LIFNR: '1000000001',
+ NETPR: '1000.00',
+ PEINH: '1',
+ BPRME: 'EA',
+ NETWR: '1000.00',
+ BRTWR: '1100.00',
+ LFDAT: '20241201',
+ EBELP: '',
+ ZCON_NO_PO: ''
+ }],
+ prReturn: [{
+ ANFNR: 'TEST001',
+ ANFPS: '00001',
+ EBELN: 'PR001',
+ EBELP: '00001',
+ MSGTY: 'S',
+ MSGTXT: 'Test message'
+ }]
})
+ // PO 아이템 관리 함수들
+ const addPoItem = () => {
+ const newItemIndex = poData.items.length + 1
+ setPoData(prev => ({
+ ...prev,
+ items: [...prev.items, {
+ ANFNR: prev.header.ANFNR,
+ ANFPS: String(newItemIndex).padStart(5, '0'),
+ LIFNR: prev.header.LIFNR,
+ NETPR: '0.00',
+ PEINH: '1',
+ BPRME: 'EA',
+ NETWR: '0.00',
+ BRTWR: '0.00',
+ LFDAT: prev.header.IHRAN,
+ EBELP: '',
+ ZCON_NO_PO: ''
+ }]
+ }))
+ }
+
+ const removePoItem = (index: number) => {
+ if (poData.items.length > 1) {
+ setPoData(prev => ({
+ ...prev,
+ items: prev.items.filter((_, i) => i !== index)
+ }))
+ }
+ }
+
+ const updatePoItem = (index: number, field: string, value: string) => {
+ setPoData(prev => ({
+ ...prev,
+ items: prev.items.map((item, i) =>
+ i === index ? { ...item, [field]: value } : item
+ )
+ }))
+ }
+
+ const updatePoHeader = (field: string, value: string) => {
+ setPoData(prev => ({
+ ...prev,
+ header: { ...prev.header, [field]: value }
+ }))
+ }
+
return (
<div className="container mx-auto py-8 space-y-6">
<div className="flex items-center justify-between">
@@ -513,87 +573,188 @@ export default function ECCSenderTestPage() {
PO (Purchase Order) 생성
</CardTitle>
<CardDescription>
- 구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE)
+ 구매주문 생성 요청을 ECC로 전송합니다. (IF_ECC_EVCP_PO_CREATE) - 하나의 PO에 여러 PR 아이템들을 포함시키는 구조
</CardDescription>
</CardHeader>
- <CardContent className="space-y-4">
+ <CardContent className="space-y-6">
+ {/* PO 헤더 정보 */}
<div className="space-y-4">
- <h4 className="font-semibold">PO 헤더 정보</h4>
- <div className="grid grid-cols-2 gap-4">
+ <div className="flex items-center justify-between">
+ <h4 className="font-semibold">PO 헤더 정보</h4>
+ <Badge variant="outline" className="text-xs">
+ {poData.items.length}개 아이템
+ </Badge>
+ </div>
+ <div className="grid grid-cols-3 gap-4">
<div className="space-y-2">
- <Label htmlFor="po-anfnr">입찰번호 (필수)</Label>
+ <Label htmlFor="po-anfnr">입찰번호 (ANFNR) <span className="text-red-500">*</span></Label>
<Input
id="po-anfnr"
- value={poData.ANFNR}
- onChange={(e) => setPoData(prev => ({ ...prev, ANFNR: e.target.value }))}
- placeholder="입찰번호"
+ value={poData.header.ANFNR}
+ onChange={(e) => updatePoHeader('ANFNR', e.target.value)}
+ placeholder="입찰번호 (ANFNR)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-lifnr">공급업체 계정 (필수)</Label>
+ <Label htmlFor="po-lifnr">공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label>
<Input
id="po-lifnr"
- value={poData.LIFNR}
- onChange={(e) => setPoData(prev => ({ ...prev, LIFNR: e.target.value }))}
- placeholder="공급업체 계정번호"
+ value={poData.header.LIFNR}
+ onChange={(e) => updatePoHeader('LIFNR', e.target.value)}
+ placeholder="공급업체 계정 (LIFNR) : 벤더코드"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-zproc">처리상태 (필수)</Label>
+ <Label htmlFor="po-zproc">처리상태 (ZPROC_IND) <span className="text-red-500">*</span></Label>
<Input
id="po-zproc"
- value={poData.ZPROC_IND}
- onChange={(e) => setPoData(prev => ({ ...prev, ZPROC_IND: e.target.value }))}
- placeholder="구매처리상태"
+ value={poData.header.ZPROC_IND}
+ onChange={(e) => updatePoHeader('ZPROC_IND', e.target.value)}
+ placeholder="처리상태 (ZPROC_IND)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-waers">통화 (필수)</Label>
+ <Label htmlFor="po-angnr">협상번호 (ANGNR)</Label>
+ <Input
+ id="po-angnr"
+ value={poData.header.ANGNR}
+ onChange={(e) => updatePoHeader('ANGNR', e.target.value)}
+ placeholder="협상번호 (ANGNR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-waers">통화 (WAERS) <span className="text-red-500">*</span></Label>
<Input
id="po-waers"
- value={poData.WAERS}
- onChange={(e) => setPoData(prev => ({ ...prev, WAERS: e.target.value }))}
- placeholder="통화 코드"
+ value={poData.header.WAERS}
+ onChange={(e) => updatePoHeader('WAERS', e.target.value)}
+ placeholder="통화 (WAERS)"
/>
</div>
- </div>
-
- <h4 className="font-semibold">PO 아이템 정보</h4>
- <div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
- <Label htmlFor="po-anfps">입찰 아이템번호 (필수)</Label>
+ <Label htmlFor="po-zterm">지급조건 (ZTERM) <span className="text-red-500">*</span></Label>
<Input
- id="po-anfps"
- value={poData.ANFPS}
- onChange={(e) => setPoData(prev => ({ ...prev, ANFPS: e.target.value }))}
- placeholder="입찰 아이템번호"
+ id="po-zterm"
+ value={poData.header.ZTERM}
+ onChange={(e) => updatePoHeader('ZTERM', e.target.value)}
+ placeholder="지급조건 (ZTERM)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-netpr">순가격 (필수)</Label>
+ <Label htmlFor="po-inco1">인코텀즈1 (INCO1) <span className="text-red-500">*</span></Label>
<Input
- id="po-netpr"
- value={poData.NETPR}
- onChange={(e) => setPoData(prev => ({ ...prev, NETPR: e.target.value }))}
- placeholder="순가격"
+ id="po-inco1"
+ value={poData.header.INCO1}
+ onChange={(e) => updatePoHeader('INCO1', e.target.value)}
+ placeholder="인코텀즈1 (INCO1)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-inco2">인코텀즈2 (INCO2) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-inco2"
+ value={poData.header.INCO2}
+ onChange={(e) => updatePoHeader('INCO2', e.target.value)}
+ placeholder="인코텀즈2 (INCO2)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-mwskz">세금코드 (MWSKZ) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-mwskz"
+ value={poData.header.MWSKZ}
+ onChange={(e) => updatePoHeader('MWSKZ', e.target.value)}
+ placeholder="세금코드 (MWSKZ)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-bprme">주문단위 (필수)</Label>
+ <Label htmlFor="po-lands">국가키 (LANDS) <span className="text-red-500">*</span></Label>
<Input
- id="po-bprme"
- value={poData.BPRME}
- onChange={(e) => setPoData(prev => ({ ...prev, BPRME: e.target.value }))}
- placeholder="주문단위 (예: EA)"
+ id="po-lands"
+ value={poData.header.LANDS}
+ onChange={(e) => updatePoHeader('LANDS', e.target.value)}
+ placeholder="국가키 (LANDS)"
/>
</div>
<div className="space-y-2">
- <Label htmlFor="po-lfdat">납기일 (필수)</Label>
+ <Label htmlFor="po-zrcv-dt">수령일 (ZRCV_DT) <span className="text-red-500">*</span></Label>
<Input
- id="po-lfdat"
- value={poData.LFDAT}
- onChange={(e) => setPoData(prev => ({ ...prev, LFDAT: e.target.value }))}
- placeholder="YYYYMMDD 형식"
+ id="po-zrcv-dt"
+ value={poData.header.ZRCV_DT}
+ onChange={(e) => updatePoHeader('ZRCV_DT', e.target.value)}
+ placeholder="수령일 (ZRCV_DT)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zatten-ind">참석지시자 (ZATTEN_IND) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-zatten-ind"
+ value={poData.header.ZATTEN_IND}
+ onChange={(e) => updatePoHeader('ZATTEN_IND', e.target.value)}
+ placeholder="참석지시자 (ZATTEN_IND)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-ihran">입찰마감일 (IHRAN) <span className="text-red-500">*</span></Label>
+ <Input
+ id="po-ihran"
+ value={poData.header.IHRAN}
+ onChange={(e) => updatePoHeader('IHRAN', e.target.value)}
+ placeholder="입찰마감일 (IHRAN)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-text">텍스트 (TEXT)</Label>
+ <Input
+ id="po-text"
+ value={poData.header.TEXT}
+ onChange={(e) => updatePoHeader('TEXT', e.target.value)}
+ placeholder="텍스트 (TEXT)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-lstel">배송지점 (LSTEL)</Label>
+ <Input
+ id="po-lstel"
+ value={poData.header.LSTEL}
+ onChange={(e) => updatePoHeader('LSTEL', e.target.value)}
+ placeholder="배송지점 (LSTEL)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-vstel">출하지점 (VSTEL)</Label>
+ <Input
+ id="po-vstel"
+ value={poData.header.VSTEL}
+ onChange={(e) => updatePoHeader('VSTEL', e.target.value)}
+ placeholder="출하지점 (VSTEL)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zdlv-cntl">납품담당자 (ZDLV_CNTLR)</Label>
+ <Input
+ id="po-zdlv-cntl"
+ value={poData.header.ZDLV_CNTLR}
+ onChange={(e) => updatePoHeader('ZDLV_CNTLR', e.target.value)}
+ placeholder="납품담당자 (ZDLV_CNTLR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zdlv-price-note">가격노트 (ZDLV_PRICE_NOTE)</Label>
+ <Input
+ id="po-zdlv-price-note"
+ value={poData.header.ZDLV_PRICE_NOTE}
+ onChange={(e) => updatePoHeader('ZDLV_PRICE_NOTE', e.target.value)}
+ placeholder="가격노트 (ZDLV_PRICE_NOTE)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label htmlFor="po-zdlv-price-t">가격유형 (ZDLV_PRICE_T)</Label>
+ <Input
+ id="po-zdlv-price-t"
+ value={poData.header.ZDLV_PRICE_T}
+ onChange={(e) => updatePoHeader('ZDLV_PRICE_T', e.target.value)}
+ placeholder="가격유형 (ZDLV_PRICE_T)"
/>
</div>
</div>
@@ -601,6 +762,133 @@ export default function ECCSenderTestPage() {
<Separator />
+ {/* PO 아이템 정보 */}
+ <div className="space-y-4">
+ <div className="flex items-center justify-between">
+ <h4 className="font-semibold">PO 아이템 정보</h4>
+ <Button
+ onClick={addPoItem}
+ size="sm"
+ variant="outline"
+ className="flex items-center gap-1"
+ >
+ <Plus className="h-4 w-4" />
+ 아이템 추가
+ </Button>
+ </div>
+
+ {poData.items.map((item, index) => (
+ <Card key={index} className="p-4">
+ <div className="flex items-center justify-between mb-4">
+ <h5 className="font-medium">아이템 #{index + 1}</h5>
+ {poData.items.length > 1 && (
+ <Button
+ onClick={() => removePoItem(index)}
+ size="sm"
+ variant="destructive"
+ className="flex items-center gap-1"
+ >
+ <Minus className="h-4 w-4" />
+ 삭제
+ </Button>
+ )}
+ </div>
+ <div className="grid grid-cols-3 gap-4">
+ <div className="space-y-2">
+ <Label>입찰번호 (ANFNR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.ANFNR}
+ onChange={(e) => updatePoItem(index, 'ANFNR', e.target.value)}
+ placeholder="입찰번호 (ANFNR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>입찰 아이템번호 (ANFPS) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.ANFPS}
+ onChange={(e) => updatePoItem(index, 'ANFPS', e.target.value)}
+ placeholder="입찰 아이템번호 (ANFPS)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>공급업체 계정 (LIFNR) : 벤더코드 <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.LIFNR}
+ onChange={(e) => updatePoItem(index, 'LIFNR', e.target.value)}
+ placeholder="공급업체 계정 (LIFNR) : 벤더코드"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>순가격 (NETPR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.NETPR}
+ onChange={(e) => updatePoItem(index, 'NETPR', e.target.value)}
+ placeholder="순가격 (NETPR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>가격단위 (PEINH) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.PEINH}
+ onChange={(e) => updatePoItem(index, 'PEINH', e.target.value)}
+ placeholder="가격단위 (PEINH)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>주문단위 (BPRME) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.BPRME}
+ onChange={(e) => updatePoItem(index, 'BPRME', e.target.value)}
+ placeholder="주문단위 (BPRME)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>순금액 (NETWR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.NETWR}
+ onChange={(e) => updatePoItem(index, 'NETWR', e.target.value)}
+ placeholder="순금액 (NETWR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>총금액 (BRTWR) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.BRTWR}
+ onChange={(e) => updatePoItem(index, 'BRTWR', e.target.value)}
+ placeholder="총금액 (BRTWR)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>납기일 (LFDAT) <span className="text-red-500">*</span></Label>
+ <Input
+ value={item.LFDAT}
+ onChange={(e) => updatePoItem(index, 'LFDAT', e.target.value)}
+ placeholder="납기일 (LFDAT)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>구매오더 품번 (EBELP)</Label>
+ <Input
+ value={item.EBELP}
+ onChange={(e) => updatePoItem(index, 'EBELP', e.target.value)}
+ placeholder="구매오더 품번 (EBELP)"
+ />
+ </div>
+ <div className="space-y-2">
+ <Label>계약번호 (ZCON_NO_PO)</Label>
+ <Input
+ value={item.ZCON_NO_PO}
+ onChange={(e) => updatePoItem(index, 'ZCON_NO_PO', e.target.value)}
+ placeholder="계약번호 (ZCON_NO_PO)"
+ />
+ </div>
+ </div>
+ </Card>
+ ))}
+ </div>
+
+ <Separator />
+
<div className="flex gap-4">
<Button
onClick={() => runTest('PO 생성 (샘플)', () => createTestPurchaseOrder())}
@@ -614,41 +902,9 @@ export default function ECCSenderTestPage() {
<Button
onClick={() => runTest('PO 생성 (사용자)', async () => {
const poRequest = {
- T_Bidding_HEADER: [{
- ANFNR: poData.ANFNR,
- LIFNR: poData.LIFNR,
- ZPROC_IND: poData.ZPROC_IND,
- ANGNR: poData.ANGNR,
- WAERS: poData.WAERS,
- ZTERM: poData.ZTERM,
- INCO1: poData.INCO1,
- INCO2: poData.INCO2,
- MWSKZ: poData.MWSKZ,
- LANDS: poData.LANDS,
- ZRCV_DT: poData.ZRCV_DT,
- ZATTEN_IND: poData.ZATTEN_IND,
- IHRAN: poData.IHRAN,
- TEXT: poData.TEXT
- }],
- T_Bidding_ITEM: [{
- ANFNR: poData.ANFNR,
- ANFPS: poData.ANFPS,
- LIFNR: poData.LIFNR,
- NETPR: poData.NETPR,
- PEINH: poData.PEINH,
- BPRME: poData.BPRME,
- NETWR: poData.NETWR,
- BRTWR: poData.BRTWR,
- LFDAT: poData.LFDAT
- }],
- T_PR_RETURN: [{
- ANFNR: poData.ANFNR,
- ANFPS: poData.ANFPS,
- EBELN: poData.EBELN,
- EBELP: poData.EBELP,
- MSGTY: poData.MSGTY,
- MSGTXT: poData.MSGTXT
- }]
+ T_Bidding_HEADER: [poData.header],
+ T_Bidding_ITEM: poData.items,
+ T_PR_RETURN: poData.prReturn
}
return createPurchaseOrder(poRequest)
})}
@@ -656,7 +912,7 @@ export default function ECCSenderTestPage() {
>
{isLoading['PO 생성 (사용자)'] && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
<Send className="mr-2 h-4 w-4" />
- 사용자 데이터로 생성
+ 사용자 데이터로 생성 ({poData.items.length}개 아이템)
</Button>
</div>